#ifndef __H_TASKMANAGER__
#define __H_TASKMANAGER__

#include "thread.h"
#include "SafeQueue.h"
#include "../third_party/scoped_refptr/scoped_refptr.h"

namespace bl{

class ITask;

class ITaskCallback
{
public:
	virtual void TaskCancelled(ITask*) = 0;
	virtual void TaskStart(ITask*) = 0;
	virtual void TaskFinished(ITask*) = 0;
	virtual void TaskEventNotify(ITask*, int taskState, int progress) = 0;
};

class ITaskManagerContext
{
public:
	virtual bool HasRequestClose() = 0;
};

class ITask : virtual public RefCounted
{
public:
	ITask() : cancelled_event_(true, false), task_id_(-1){}
	virtual ~ITask() {}

	unsigned int GetTaskId() const { return task_id_; }
	void put_TaskId(unsigned int taskId) { task_id_ = taskId; }

	void Cancel()
	{
		cancelled_event_.Set();
	}

	bool IsCancelled()
	{
		return cancelled_event_.Wait(0);
	}

	virtual bool Execute(ITaskManagerContext* context, ITaskCallback* callback) = 0;

protected:
	bl::Event cancelled_event_;
	unsigned int task_id_;
};

template<int DefaultMaxTasks = 100>
class TaskManager : public bl::IRunner, protected bl::SafeQueue<ITask*>, public ITaskManagerContext
{
public:
	TaskManager(ITaskCallback* taskCallback) : bl::SafeQueue<ITask*>(DefaultMaxTasks), task_id_sequence_(0)
	{
		call_back_ = taskCallback;
		keep_running_ = true;
		main_thread_.Start(this);
	}

	bool AddTask(ITask* task)
	{
		task->put_TaskId(task_id_sequence_++);
		task->Ref();
		return Push(task);
	}

	// If a task is in progress or finished, it can't be removed here.
	bool RemoveTask(ITask* task)
	{
		if(Remove(task))
		{
			task->Unref();
			return true;
		}

		return false;
	}

	bool RemoveTask(unsigned int taskId)
	{
		bool bRemoved = false;
		bool bFindinQueue = false;
		ITask* task = NULL;
		{
			bl::AutoLockCS lock(crit_sec_);
			for(std::list<ITask*>::iterator ite = list_.begin(); ite!=list_.end(); ite++)
			{
				if((*ite)->GetTaskId() == taskId)
				{
					bFindinQueue = true;
					task = *ite;
					break;
				}
			}
		}

		if(bFindinQueue)
		{
			if(Remove(task))
			{
				task->Unref();
				bRemoved = true;
			}
		}
		else
		{
			bl::AutoLockCS lock(internal_cs_);
			if(in_process_task_ && (taskId==in_process_task_->GetTaskId()) )
			{
				in_process_task_->Cancel();
				bRemoved = true;
			}
		}

		return bRemoved;
	}

	bool HasRequestClose()
	{
		return !keep_running_;
	}

	void Close()
	{
		keep_running_ = false;
		Shutdown();
		main_thread_.WaitUntilExit();

		for(std::list<ITask*>::iterator ite = list_.begin(); ite!=list_.end(); ite++)
		{
			(*ite)->Unref();
		}
	}

protected:
	virtual void Run()
	{
		while(keep_running_)
		{
			ITask* task = NULL;
			bool ret = Pop(&task);
			if(ret)
			{
				if(!task->IsCancelled())
				{
					{
						bl::AutoLockCS lock(internal_cs_);
						in_process_task_ = task;
					}

					call_back_->TaskStart(task);

					bool bOk = task->Execute(this, call_back_);

					if(bOk)
						call_back_->TaskFinished(task);
					else
						call_back_->TaskCancelled(task);

					{
						bl::AutoLockCS lock(internal_cs_);
						in_process_task_.reset(NULL);
					}

				}
				task->Unref();
			}
		}
	}

private:
	bl::Thread main_thread_;
	volatile bool keep_running_;
	ITaskCallback* call_back_;
	unsigned int task_id_sequence_;

	scoped_refptr<ITask> in_process_task_;
	bl::CritSec internal_cs_;
};

}

#endif